package fun.xinnix.aop;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import cn.hutool.json.JSONUtil;
import fun.xinnix.annotation.Log;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Slf4j
@Component
@Aspect
public class LogAdvice {
    @Pointcut("@annotation(fun.xinnix.annotation.Log)")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        TimeInterval timer = DateUtil.timer();
        Object result = joinPoint.proceed();
        saveLog(joinPoint, result, timer.intervalRestart());
        return result;
    }

    /**
     * 1
     *
     * @param joinPoint
     */
    @Before("pointcut()")
    public void before(JoinPoint joinPoint) {
        log.info("before advice");
    }

    /**
     * 3
     *
     * @param joinPoint
     */
    @After("pointcut()")
    public void after(JoinPoint joinPoint) {
        log.info("after advice");
    }

    /**
     * 2
     *
     * @param joinPoint
     * @param result
     */
    @AfterReturning(value = "pointcut()", returning = "result")
    public void afterReturning(JoinPoint joinPoint, Object result) {
        log.info("afterReturning:{}", JSONUtil.toJsonStr(result));
    }

    /**
     * 如果抛出异常,则会在前置通知后调到
     *
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(value = "pointcut()", throwing = "e")
    public void afterReturningMethod(JoinPoint joinPoint, Exception e) {
        log.info("afterReturningMethod");
    }

    public void saveLog(ProceedingJoinPoint joinPoint, Object obj, long time) {
        @Data
        class LogInfo {
            String action;
            String className;
            String methodName;
            Object[] argList;
        }
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Log annotation = method.getAnnotation(Log.class);
        LogInfo logInfo = new LogInfo();
        if (annotation != null) {
            logInfo.setAction(annotation.value());
        }
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = signature.getName();
        Object[] args = joinPoint.getArgs();
        logInfo.setClassName(className);
        logInfo.setMethodName(methodName);
        logInfo.setArgList(args);
        log.info("logInfo:{},obj:{},time:{}", JSONUtil.toJsonStr(logInfo), obj, time);
    }
}
